﻿using System;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using BBCode.Collections;
using BBCode.Tags;
using BBCode.Template;

namespace BBCode.Nodes
{
    /// <summary>
    /// BBCode
    /// </summary>
    public class BBCode : INestableNode
    {
        private readonly string _closing;
        private readonly string _id = "[" + Guid.NewGuid() + "]";
        private readonly string _innerText;
        private readonly NodeIndex _nodeIndex;
        private readonly string _opening;
        private readonly ITag _tag;
        private readonly string _text;
        private NodeCollection _children;

        #region properties

        /// <summary>
        /// Gets a value indicating whether this instance has parent.
        /// </summary>
        /// <value>
        /// 	<c>true</c> if this instance has parent; otherwise, <c>false</c>.
        /// </value>
        public bool HasParent
        {
            get { return Parent != null; }
        }

        /// <summary>
        /// Gets the ID of the Node.
        /// </summary>
        public string ID
        {
            get { return _id; }
        }

        /// <summary>
        /// Gets the inner text.
        /// </summary>
        public string InnerText
        {
            get { return _innerText; }
            //private set { _innerText = value; }
        }

        /// <summary>
        /// Gets or sets the children of the Node.
        /// </summary>
        /// <value>
        /// The children.
        /// </value>
        public NodeCollection Children
        {
            get
            {
                _children = _children ?? new NodeCollection();
                return _children;
            }
            set { _children = value; }
        }

        /// <summary>
        /// Gets a value indicating whether this instance has children.
        /// </summary>
        /// <value>
        /// 	<c>true</c> if this instance has children; otherwise, <c>false</c>.
        /// </value>
        public bool HasChildren
        {
            get { return _children != null; }
        }

        /// <summary>
        /// Gets or sets the parent.
        /// </summary>
        /// <value>
        /// The parent.
        /// </value>
        public INode Parent { get; set; }

        /// <summary>
        /// Gets the tag.
        /// </summary>
        public ITag Tag
        {
            get { return _tag; }
        }

        /// <summary>
        /// Gets the index of the node.
        /// </summary>
        /// <value>
        /// The index of the node.
        /// </value>
        public NodeIndex NodeIndex
        {
            get { return _nodeIndex; }
        }

        /// <summary>
        /// Gets the text.
        /// </summary>
        public string Text
        {
            get { return _text; }
        }

        /// <summary>
        /// Gets the opening.
        /// </summary>
        public string Opening
        {
            get { return _opening; }
        }

        /// <summary>
        /// Gets the closing.
        /// </summary>
        public string Closing
        {
            get { return _closing; }
        }

        #endregion

        #region constructors

        /// <summary>
        /// Initializes a new instance of the <see cref="BBCode"/> class.
        /// </summary>
        /// <param name="tag">The tag.</param>
        /// <param name="text">The text.</param>
        /// <param name="nodeIndex">Index of the node.</param>
        /// <param name="opening">The opening tag.</param>
        /// <param name="closing">The closing tag.</param>
        public BBCode(ITag tag, string text, NodeIndex nodeIndex, string opening, string closing)
        {
            _text = text;
            _innerText = text.Remove(0, opening.Length);
            _innerText = _innerText.Remove(_innerText.Length - closing.Length, closing.Length);
            _closing = closing;
            _opening = opening;
            _nodeIndex = nodeIndex;
            _tag = tag;
        }

        #endregion

        #region INestableNode Members

        /// <summary>
        /// Prints the specific Node.
        /// </summary>
        /// <param name="shouldAutoParseLinks">if set to <c>true</c> [should auto parse links].</param>
        /// <returns></returns>
        public virtual string Print(bool shouldAutoParseLinks = true)
        {
            var textBuilder = new StringBuilder(Text);

            //Remove the opening and closing
            textBuilder.Remove(0, Opening.Length);
            textBuilder.Remove(textBuilder.Length - Closing.Length, Closing.Length);

            //Get the template, fill it and process it!.
            switch (Tag.ParseType)
            {
                case ParseType.Clean:
                    //Clean the node, leaving the innertext
                    return textBuilder.ToString();
                    break;
                case ParseType.Remove:
                    //Remove it ALL!
                    return string.Empty;
                default:
                    //Default is always to print it as it should be printed!
                    var template = Tag.GetTemplate();
                    if (HasChildren)
                    {
                        //Go through the nodes, in the right order
                        var orderedNodes = from n in Children
                                                                 orderby n.Tag.Priority descending
                                                                 select n;

                        //Set the ID's of all the nodes
                        foreach (INode node in orderedNodes)
                        {
                            int firstIndex = textBuilder.ToString().IndexOf(node.Text);
                            textBuilder.Replace(node.Text, node.ID, firstIndex, node.Text.Length);
                        }
                    }

                    //Check if we allow children
                    if (Tag.CanHaveChildren)
                    {
                        //Auto parsing Links
                        if (shouldAutoParseLinks)
                        {
                            var matches = Regex.AutoParseRegex.Matches(textBuilder.ToString());

                            foreach (Match m in matches)
                            {
                                textBuilder.Replace(m.Value,
                                                    "<a href=\"" + m.Value + "\" rel=\"nofollow\">" + m.Value + "</a>");
                            }
                        }
                    }

                    //Replace the ID's with the right text
                    foreach (var node in Children)
                    {
                        textBuilder.Replace(node.ID, node.Print(shouldAutoParseLinks));
                    }

                    template["INNERTEXT"] = textBuilder;

                    return template.Process();
            }
        }

        #endregion
    }
}